﻿using Microsoft.Extensions.Caching.Memory;
using System.Collections.Concurrent;
using System.Collections.Generic;

namespace ORS.OrderRobot.Cache
{
    /// <summary>
    /// 内存缓存实现
    /// </summary>
    public class MemoryCache : ICache
    {
        private readonly IMemoryCache _cache;
        private static readonly ConcurrentDictionary<string, byte> _keys = new ConcurrentDictionary<string, byte>();
        private static readonly TimeSpan DefaultExpiration = TimeSpan.FromHours(24); // 默认24小时过期

        public MemoryCache(IMemoryCache cache)
        {
            _cache = cache ?? throw new ArgumentNullException(nameof(cache));
        }

        public async Task SetAsync(string key, object value, TimeSpan? expiration = null)
        {
            var options = new MemoryCacheEntryOptions();
            options.AbsoluteExpirationRelativeToNow = expiration ?? DefaultExpiration;
            options.RegisterPostEvictionCallback(PostEvictionCallback);

            _cache.Set(key, value, options);
            _keys.TryAdd(key, 0);
            await Task.CompletedTask;
        }

        public Task<T> GetAsync<T>(string key)
        {
            return Task.FromResult(_cache.TryGetValue(key, out T value) ? value : default);
        }

        public async Task<T> GetOrCreateAsync<T>(string key, Func<Task<T?>> factory, TimeSpan? expiration = null)
        {
            if (_cache.TryGetValue(key, out T value))
            {
                return value;
            }

            value = await factory();
            if(value!=null) await SetAsync(key, value, expiration);
            return value;
        }

        public Task RemoveAsync(string key)
        {
            _cache.Remove(key);
            _keys.TryRemove(key, out _);
            return Task.CompletedTask;
        }

        public Task<IEnumerable<string>> GetAllKeysAsync(string? prefix = null)
        {
            var keys = _keys.Keys;
            if (!string.IsNullOrEmpty(prefix))
            {
                keys = keys.Where(x => x.StartsWith(prefix)).ToList();
            }
            return Task.FromResult<IEnumerable<string>>(keys);
        }

        private void PostEvictionCallback(object key, object value, EvictionReason reason, object state)
        {
            if (key is string keyString && reason != EvictionReason.Replaced)
            {
                _keys.TryRemove(keyString, out _);
            }
        }
    }
}

